import sys
import time
from src.common.core import TypeCast
from src.common.utils.serial.SerialUtil import SerialUtil
from src.protocol import ProtocolConst, SerialProtocol
from src.common.core.dispatcher import DispatcherBase
from src.common.utils.websocket.WebSocketUtil import WebSocketUtil
from src.common.utils.thread.threadUtil import ThreadUtil
from src.views.UIHomeView import *


class HomeController:
    def __init__(self):
        self.wsUtil = WebSocketUtil()
        self.wsUtil.onOpen = self.onOpen
        self.wsUtil.onMessage = self.onMessage
        self.wsUtil.onError = self.onError
        self.wsUtil.onClose = self.onClose
        DispatcherBase.instance.add(ProtocolConst.Device_GetParams, self.getParamsCallBack)
        DispatcherBase.instance.add(ProtocolConst.Device_SetParams, self.setParamsCallBack)
        DispatcherBase.instance.add(ProtocolConst.Server_ServerReceive, self.ServerReceiveCallBack)
        self.wsUtil.connect(config.server.websocketUrl+"admin:1")

        self.serUtil = SerialUtil()
        self.serUtil.onMessage = self.onDeviceMessage
        self.serUtil.timeout = config.serial.timeout
        self.serUtil.readType = config.serial.readType
        self.serUtil.readLen = config.serial.readLen
        self.serUtil.readLine = config.serial.readLine

        self.ui = UIHomeView()
        self.ui.showMaximized()
        self.ui.timer_camera.start(30)
        self.ui.lightSwichPBClickCallback = self.lightSwichPBClickCallback
        self.ui.lightPowerPBClickCallback = self.lightPowerPBClickCallback
        self.ui.motorStateSwichPBClickCallback = self.motorStateSwichPBClickCallback
        self.ui.motorStateSwich2PBClickCallback = self.motorStateSwich2PBClickCallback
        self.ui.metersLenghtResetPBClickCallback = self.metersLenghtResetPBClickCallback
        self.ui.CBClickCallback = self.CBClickCallback
        self.ui.speedSavePBClickCallback = self.speedSavePBClickCallback

        if not self.serUtil.open(config.serial.port, config.serial.bps):
            self.ui.message.critical(self.ui, "失败", "串口打开失败")
        self.serUtil.subByThreadStart()
        self.dataMsg = b"\x5a\x81\x00\x31\x83"
        thread = ThreadUtil()
        def getDeviceParams():
            while True:
                try:
                    if self.serUtil.ser: self.serUtil.pub(self.dataMsg)
                except Exception as e:
                    self.serUtil.close()
                    self.serUtil.open()
                time.sleep(1)
        thread.startThread(getDeviceParams)
        # self.serUtil.pub(data)
        self.ui.setUI(SerialProtocol.instance.toDict())

        thread = ThreadUtil()

        def cameraCheck():
            while True:
                try:
                    if self.ui.cameraCheck():
                        time.sleep(3)
                    else:
                        time.sleep(1)
                except Exception as e:
                    pass
        thread.startThread(cameraCheck)
        self.ui.timer_camera.start(int(1000 / config.camera.fps))

        thread = ThreadUtil()

        def writeCameraData():
            # count = 0
            # lastTime = time.time()
            while True:
                if len(self.ui.cameraData) > 0 and self.ui.videoWriter!=None:
                    # count += 1
                    # if (time.time() - lastTime >= 1):
                        # print(f"write:{count}")
                        # count = 0
                        # lastTime = time.time()
                    self.ui.videoWriter.write(self.ui.cameraData[0])
                    del self.ui.cameraData[0]
        thread.startThread(writeCameraData)

        thread = ThreadUtil()
        thread.startThread(self.ui.getCameraFrame)

    # region websocket method
    def onOpen(self, ws):
        print(f"连接成功")

    def onMessage(self, ws, msg):
        # print(f"接收到消息：{msg}")
        null = None
        jsonMsg = eval(msg)
        DispatcherBase.instance.dispatch(jsonMsg['protocolId'], jsonMsg)

    def onError(self, ws, error):
        print(f"发生错误：{error}")

    def onClose(self, ws, code, msg):
        print(f"断开连接:{code}-{msg}")
    # endregion

    # region serial method
    def onDeviceMessage(self, data):
        print(f"串口接收到{len(data)}位数据：{data}")
        try:
            SerialProtocol.instance.parseProtoData(data)
            self.ui.setUI(SerialProtocol.instance.toDict())
        except Exception as e:
            print(e)

    def pub(self, data):
        if (self.serUtil.ser):
            try:
                self.serUtil.pub(data)
            except Exception as e:
                self.ui.message.critical(self.ui, "串口异常", f"串口未开启或被拔出。{e}")
        else:
            self.ui.message.critical(self.ui, "串口异常", "串口未开启或被拔出。")
    # endregion

    # region websocket 相关回调
    def getParamsCallBack(self, data=None):
        data = SerialProtocol.instance.toString()
        msg = f'{{"protocolId": {ProtocolConst.Device_GetParamsReturn}, "data": {data},"meta": {{"msg": "服务器返回设备状态成功","status": 200}}}}'
        self.wsUtil.ws.send(msg)

    def setParamsCallBack(self, data):
        newProto = SerialProtocol.clone()
        data = data['data']
        key = data['key']
        value = data['value']

        if (key == 'meters'):
            cmd = f"newProto.{key}=2114"
        else:
            cmd = f"newProto.{key}={value}"
        exec(cmd)
        if (key == 'meters'):
            self.pub(newProto.makeProtoData())
        else:
            self.pub(newProto.makeProtoData())
            self.dataMsg = newProto.makeProtoData()
        # SerialProtocol.instance.useProtocolDict(newProto.toDict())
        # self.ui.setUI(newProto.toDict())
        msg = f'{{"protocolId": {ProtocolConst.Device_SetParamsReturn},"data": {data}, "meta": {{"msg": "状态设置成功", "status": 200}}}}'

        #msg = f'{{"protocolId": {ProtocolConst.Device_SetParamsReturn},"data": {data}, "meta": {{"msg": "状态设置失败", "status": 500}}}}'
        self.wsUtil.ws.send(str(msg).replace("'", '"'))

    def ServerReceiveCallBack(self, data):
        print(f"发送消息成功：{data}")

    # endregion

    # region 按钮点击回调
    def lightSwichPBClickCallback(self, index):
        newProto = SerialProtocol.clone()
        if eval(f'newProto.light_swich{index}'):
            exec(f'newProto.light_swich{index}=0')
        else:
            exec(f'newProto.light_swich{index}=1')
        print(newProto.toDict())
        self.pub(newProto.makeProtoData())
        self.dataMsg = newProto.makeProtoData()
        # SerialProtocol.instance.useProtocolDict(newProto.toDict())
        # self.ui.setUI(newProto.toDict())

    def lightPowerPBClickCallback(self, index, value):
        newProto = SerialProtocol.clone()
        if eval(f'newProto.light_swich{index}') and eval(f'newProto.light_power{index}')!=value: return self.ui.message.critical(self.ui, "操作错误", f"请先关闭灯{index}的开关再调整功率")
        if(eval(f'newProto.light_power{index}') == value):
            exec(f'newProto.light_power{index}=0')
        else:
            exec(f'newProto.light_power{index}={value}')
        self.pub(newProto.makeProtoData())
        self.dataMsg = newProto.makeProtoData()
        # SerialProtocol.instance.useProtocolDict(newProto.toDict())
        # self.ui.setUI(newProto.toDict())
        print(newProto.toDict())

    def motorStateSwichPBClickCallback(self, value):
        newProto = SerialProtocol.clone()
        if value:
            if self.ui.motorState2:  # 11 - 01
                newProto.motorState = 0
            else:  # 10 - 00
                newProto.motorState = 3
        else:
            if self.ui.motorState2:  # 01 - 11
                newProto.motorState = 1
            else:  # 00 - 10
                newProto.motorState = 2
        self.pub(newProto.makeProtoData())
        self.dataMsg = newProto.makeProtoData()
        # SerialProtocol.instance.useProtocolDict(newProto.toDict())
        # self.ui.setUI(newProto.toDict())
        print(newProto.toDict())

    def motorStateSwich2PBClickCallback(self, value):
        newProto = SerialProtocol.clone()
        if value:  # 11 - 10
            if self.ui.motorState1:
                newProto.motorState = 2
            else:  # 01 - 00
                newProto.motorState = 3
        else:  # 10 - 11
            if self.ui.motorState1:
                newProto.motorState = 1
            else:  # 00 - 01
                newProto.motorState = 0
        self.pub(newProto.makeProtoData())
        self.dataMsg = newProto.makeProtoData()
        # SerialProtocol.instance.useProtocolDict(newProto.toDict())
        # self.ui.setUI(newProto.toDict())
        print(newProto.toDict())

    def metersLenghtResetPBClickCallback(self):
        newProto = SerialProtocol.clone()

        newProto.meters = '2114'

        self.pub(newProto.makeProtoData())
        thread = ThreadUtil()
        thread.startThread(lambda:self.checkmetersLenghtReset(newProto.makeProtoData()))
        # self.dataMsg = newProto.makeProtoData()
        # SerialProtocol.instance.useProtocolDict(newProto.toDict())
        # self.ui.setUI(newProto.toDict())
        print(newProto.toDict())

    def CBClickCallback(self, key, status):
        newProto = SerialProtocol.clone()

        # if (index == 0):
        #     exec(f'newProto.{key}=1')
        # elif (index == 1):
        #     exec(f'newProto.{key}=2')
        # elif (index == 2):
        #     exec(f'newProto.{key}=5')
        # elif (index == 3):
        #     exec(f'newProto.{key}=7')

        if (status):
            if (key == "motor"):
                exec(f'newProto.{key}=1')
            elif (key == "gasPump"):
                exec(f'newProto.{key}=2')
        else:
            exec(f'newProto.{key}=5')

        self.pub(newProto.makeProtoData())
        self.dataMsg = newProto.makeProtoData()
        # SerialProtocol.instance.useProtocolDict(newProto.toDict())
        # self.ui.setUI(newProto.toDict())
        print(newProto.toDict())

    def speedSavePBClickCallback(self, key, speed):
        newProto = SerialProtocol.clone()

        exec(f'newProto.{key}Speed={speed}')

        self.pub(newProto.makeProtoData())
        self.dataMsg = newProto.makeProtoData()
        # SerialProtocol.instance.useProtocolDict(newProto.toDict())
        # self.ui.setUI(newProto.toDict())
        print(newProto.toDict())
    # endregion

    def checkmetersLenghtReset(self, data):
        while True:
            time.sleep(1)
            if(SerialProtocol.instance.meters == 0):
                return
            self.pub(data)




